/*
  Losu Copyright Notice
  --------------------
    Losu is an open source programming language project under the MIT license
  that can be used for both academic and commercial purposes. There are no
  fees, no royalties, and no GNU-like restrictions. Losu qualifies
  as open source software. However, Losu is not public property, and founder
  'chen-chaochen' retains its copyright.

    Losu has been registered with the National Copyright Administration of the
  People's Republic of China, and adopts the MIT license as the copyright
  licensing contract under which the right holder conditionally licenses its
  reproduction, distribution, and modification rights to an unspecified public.

    If you use Losu, please follow the public MIT agreement or choose to enter
  into a dedicated license agreement with us.

  The MIT LICENSE is as follows
  --------------------
  Copyright  2020  chen-chaochen

    Permission is hereby granted, free of charge, to any person obtaining a
  copy of this software and associated documentation files (the “Software”), to
  deal in the Software without restriction, including without limitation the
  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  sell copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
*/

#ifndef FILE_SRC_LOSU_H
#define FILE_SRC_LOSU_H

/**
 * include head file segment
 * */
#include <setjmp.h>
#include <stddef.h>
#include <stdint.h>

/**
 * cfg segment
 * */
#include "cfg/losu.cfg"
#ifndef FILE_CFG_LOSU_ALL
#error "file 'cfg/losu.cfg' not found "
#endif

/**
 * losu ctype segment
 * */
#if 1

/* losu_extern_t */
#ifndef losu_extern_t
#ifdef __cplusplus
#define losu_extern_t extern "C"
#else
#define losu_extern_t extern
#endif
#endif
/* losu_ctype_xxx */
typedef double losu_ctype_number;
typedef uint8_t losu_ctype_bool;
typedef size_t losu_ctype_size_t;
typedef int64_t losu_ctype_ssize_t;
typedef uint32_t losu_ctype_hash_t;
typedef uint64_t losu_ctype_vmins_t;

/* losu type */
typedef enum losu_object_type {
  losu_object_type_null,      /* null */
  losu_object_type_true,      /* true */
  losu_object_type_number,    /* number */
  losu_object_type_string,    /* string */
  losu_object_type_function,  /* function */
  losu_object_type_unit,      /* unit */
  losu_object_type_pointer,   /* pointer */
  losu_object_type_coroutine, /* coroutine */
  losu_object_type_context,   /* context */
  losu_object_type_unknown,   /* unknown */
} losu_object_type;

#endif

/**
 * losu signal segment
 *    api: losu_signal.h
 * */
#if 1

/* signal */
typedef uint8_t losu_signal_t;
typedef uint8_t losu_sigmsg_t;
#define losu_signal_done ((losu_signal_t)0)  /* done  */
#define losu_signal_error ((losu_signal_t)1) /* throw error */
#define losu_signal_yield ((losu_signal_t)2) /* yield signal*/
#define losu_signal_kill ((losu_signal_t)3)  /* kill over */
/* signal-message */
/* #define losu_sigmsg_none ((losu_sigmsg_t)0b0)
#define losu_sigmsg_io ((losu_sigmsg_t)0b1)
#define losu_sigmsg_vm ((losu_sigmsg_t)0b10)
#define losu_sigmsg_syntax ((losu_sigmsg_t)0b100)
#define losu_sigmsg_file ((losu_sigmsg_t)0b1000)
#define losu_sigmsg_memory ((losu_sigmsg_t)0b10000)
#define losu_sigmsg_extension ((losu_sigmsg_t)0b100000)
#define losu_sigmsg_total 7 */

#endif

/**
 * losu vm segment
 *    with: pacakeg & handle
 *    api: losu_vm.h
 */
#if 1

typedef struct losu_vm {
  /* vm-name */
  const char* name;
  /* stack */
  struct losu_object *stack, *top, *stackmax;    /* now stack */
  struct losu_object *mstack, *mtop, *mstackmax; /*  main stack */
  struct losu_object* base;                      /* base pointer */
  /* signal */
  struct __losu_vmsig {
    jmp_buf jmpflag;
  }* errjmp;
  /* data segment */
  struct losu_object_scode* inspool;      /* script pool */
  struct losu_object_function* funcpool;  /* function pool */
  struct losu_object_hash* hashpool;      /* hash pool */
  struct losu_object_context* callpool;   /* callinfo pool, for  coroutine */
  struct losu_object_coroutine* coropool; /*  coroutine pool */
  struct __losu_vm_strpool {
    losu_ctype_hash_t size;
    losu_ctype_hash_t nsize;
    struct losu_object_string** strobj; /* strobj[] */
  } strpool;                            /* string pool */
  struct losu_object_hash* global;      /* global */
  /* gc */
  losu_ctype_size_t gcDymax;
  losu_ctype_size_t nblocks;
  losu_ctype_size_t gcMax;
  losu_ctype_size_t gcHook;
  /* buff */
  unsigned char* bufftmp;
  losu_ctype_size_t nbufftmp;
  /* yield */
  losu_ctype_bool yield; /* yiled? 0, 1, 2 */
} losu_vm, *losu_vm_t;

typedef struct __losu_vmsig __losu_vmsig, *__losu_vmsig_t;

typedef struct __losu_vm_strpool __losu_vm_strpool, *__losu_vm_strpool_t;

typedef int32_t (*losu_vmapi_t)(losu_vm_t vm);

/* package & handle */
typedef struct losu_vmpkg_load {
  const char* name;
  losu_vmapi_t load;
} losu_vmpkg_load, *losu_vmpkg_load_t;

typedef struct losu_vmpkg_package {
  const char* name;
  losu_vmpkg_load_t (*load)(struct losu_vm*);
} losu_vmpkg_package, *losu_vmpkg_package_t;

#endif

/**
 * losu object type segment
 * */
#if 1

/* losu object */
typedef struct losu_object {
  uint8_t type;
  union {
    struct losu_object_string* str;
    struct losu_object_function* func;
    struct losu_object_hash* hash;
    struct losu_object_context* context;
    struct losu_object_coroutine* coro;
    losu_ctype_number num;
    void* ptr;
  } value;
} losu_object, *losu_object_t;

/* string */
typedef struct losu_object_string {
  losu_ctype_hash_t hash;
  int32_t cstidx;
  size_t len;
  struct losu_object_string* next;
  int16_t marked;
  char str[1]; /* char* */
} losu_object_string, *losu_object_string_t;

/* func */
typedef struct losu_object_function /* need: losu_object_scode */
{
  union func {
    losu_vmapi_t capi;
    struct losu_object_scode* sdef;
  } func;
  struct losu_object_function* next;
  struct losu_object_function* mark;
  losu_ctype_bool isC;
  uint8_t type; /* 0-func,1-async,2-class,3-decorators */
  int32_t nclosure;
  struct losu_object closure[1]; /* closure */
} losu_object_function, *losu_object_function_t;

typedef struct losu_object_scode {
  /* local segment */
  losu_ctype_number* lcnum;
  struct losu_object_string** lcstr;
  struct losu_object_scode** lcscode;
  losu_ctype_vmins_t* code;
  struct losu_objscode_locvar* localvar;
  int32_t* lineinfo;
  uint32_t nlcnum, nlcstr, nlcscode, ncode, nlocalvar, nlineinfo;

  int16_t narg;
  losu_ctype_bool isVarg;
  int16_t maxstacksize;
  int32_t defedline;

  struct losu_object_scode* next;

  losu_ctype_bool marked;

} losu_object_scode, *losu_object_scode_t;

typedef struct losu_objscode_locvar {
  struct losu_object_string* name;
  int32_t startpc;
  int32_t endpc;
} losu_objscode_locvar, *losu_objscode_locvar_t;

/* hash */
typedef struct losu_object_hash /* need: losu_hash_node */
{
  int32_t size;
  struct losu_hash_node* node;
  struct losu_hash_node* free;
  struct losu_object_hash* next;
  struct losu_object_hash* mark;
} losu_object_hash, *losu_object_hash_t;

typedef struct losu_hash_node {
  losu_object key;
  losu_object value;
  struct losu_hash_node* next;
} losu_hash_node, *losu_hash_node_t;

/* context */
typedef struct losu_object_context {
  struct losu_object_function* func;
  losu_ctype_vmins_t* pc;
  struct losu_object* base;
  int32_t nres;
  void (*inlinecall)(struct losu_vm* vm,
                     struct losu_object_context* context,
                     struct losu_object* func,
                     int32_t nres);
  struct losu_object* nextobj; /* context link */
  struct losu_object_context *pre, *next;
  struct losu_object* assert; /* assert back */
  losu_ctype_bool marked;

} losu_object_context, *losu_object_context_t;

/* coroutine */
typedef uint8_t losu_objcoro_state_t;

typedef struct losu_object_coroutine {
#ifndef Config_losu_vm_coro_stacksize
#define Config_losu_vm_coro_stacksize 1024
#endif
  losu_object stack[Config_losu_vm_coro_stacksize];
  losu_object_t top;
  losu_objcoro_state_t sta;
  struct losu_object_coroutine* next;
} losu_object_coroutine, *losu_object_coroutine_t;

#endif

/**
 * losu api segment
 *    with: losu_api.c
 */
#if 1

/* vm apis */
#if 1

losu_extern_t losu_vm_t vm_create(int32_t stacksize);

losu_extern_t int32_t vm_init(losu_vm_t vm,
                              losu_vmpkg_package_t pkgs_t,
                              int32_t argc,
                              const char** argv);

losu_extern_t void vm_error(losu_vm_t vm, const char* estr, ...);

losu_extern_t void vm_warning(losu_vm_t vm, const char* estr, ...);

losu_extern_t void vm_stop(losu_vm_t vm, const char* estr, ...);

losu_extern_t void vm_assert(losu_vm_t vm, losu_object_t obj[]);

losu_extern_t int32_t vm_dofile(losu_vm_t vm, const char* filename);

losu_extern_t int32_t vm_dostring(losu_vm_t vm,
                                  const char* str,
                                  const char* name);

losu_extern_t int32_t vm_dobyte(losu_vm_t vm,
                                const char* byte,
                                losu_ctype_size_t len,
                                const char* name);

losu_extern_t int32_t vm_execute(losu_vm_t vm,
                                 int32_t narg,
                                 int32_t nres,
                                 const char* name);

losu_extern_t losu_object_t vm_getval(losu_vm_t vm, const char* name);

losu_extern_t void vm_setval(losu_vm_t vm, const char* name, losu_object val);

losu_extern_t void vm_close(losu_vm_t vm);

#endif

/* gc api */
#if 1
losu_extern_t void gc_setmax(losu_vm_t vm, losu_ctype_size_t size);

losu_extern_t losu_ctype_size_t gc_getmemNow(losu_vm_t vm);

losu_extern_t losu_ctype_size_t gc_getmemMax(losu_vm_t vm);

losu_extern_t losu_ctype_bool gc_collect(losu_vm_t vm);

#endif

/* stack api */
#if 1
losu_extern_t void stack_push(losu_vm_t vm, losu_object o);

losu_extern_t void stack_pop(losu_vm_t vm, int32_t i);

losu_extern_t void stack_call(losu_vm_t vm, int32_t narg, int32_t nres);

losu_extern_t void stack_await(losu_vm_t vm, losu_object_t coto);

losu_extern_t void stack_yield(losu_vm_t vm, int32_t nres);

#endif

/* obj api */
#if 1

/* obj->type api */
#if 1
losu_extern_t int32_t obj_type(losu_vm_t vm, losu_object_t obj);

losu_extern_t const char* obj_type2str(int32_t t);
#endif

/* obj->null api */
losu_extern_t losu_object obj_newnull(losu_vm_t vm);

/* obj->number api */
#if 1
losu_extern_t losu_object obj_newnum(losu_vm_t vm, losu_ctype_number num);

losu_extern_t losu_ctype_number obj_tonum(losu_vm_t vm, losu_object_t obj);
#endif

/* obj->string api */
#if 1
losu_extern_t losu_object obj_newstr(losu_vm_t vm, char* str);

losu_extern_t losu_object obj_newstrlen(losu_vm_t vm,
                                        char* str,
                                        losu_ctype_size_t len);

losu_extern_t losu_ctype_size_t obj_getstrlen(losu_vm_t vm, losu_object_t obj);

losu_extern_t const char* obj_tostr(losu_vm_t vm, losu_object_t obj);
#endif

/* obj->func */
#if 1
losu_extern_t losu_object obj_newfunc(losu_vm_t vm, losu_vmapi_t func);

losu_extern_t losu_vmapi_t obj_tofunc(losu_vm_t vm, losu_object_t obj);
#endif

/* obj->unit */
#if 1
losu_extern_t losu_object obj_newunit(losu_vm_t vm);

losu_extern_t losu_object_t obj_unit_index(losu_vm_t vm,
                                           losu_object unit,
                                           losu_object key);

losu_extern_t losu_object_t obj_unit_indexnum(losu_vm_t vm,
                                              losu_object unit,
                                              losu_ctype_number i);

losu_extern_t losu_object_t obj_unit_indexstr(losu_vm_t vm,
                                              losu_object unit,
                                              const char* s);

losu_extern_t void obj_unit_set(losu_vm_t vm,
                                losu_object unit,
                                losu_object key,
                                losu_object value);

losu_extern_t void obj_unit_setnum(losu_vm_t vm,
                                   losu_object unit,
                                   losu_ctype_number key,
                                   losu_object value);

losu_extern_t void obj_unit_setstr(losu_vm_t vm,
                                   losu_object unit,
                                   const char* key,
                                   losu_object value);

losu_extern_t losu_hash_node_t obj_unit_first(losu_vm_t vm, losu_object unit);

losu_extern_t losu_hash_node_t obj_unit_next(losu_vm_t vm,
                                             losu_object unit,
                                             losu_hash_node_t node);

losu_extern_t losu_object obj_unit_nodekey(losu_vm_t vm, losu_hash_node_t node);

losu_extern_t losu_object obj_unit_nodeval(losu_vm_t vm, losu_hash_node_t node);
#endif

/* obj->pointer */
#if 1
losu_extern_t losu_object obj_newptr(losu_vm_t vm, void* ptr);

losu_extern_t void* obj_toptr(losu_vm_t vm, losu_object_t obj);
#endif

/* obj->coro */
#if 1
losu_extern_t losu_object obj_newcoro(losu_vm_t vm,
                                      losu_object_t func,
                                      int32_t narg,
                                      losu_object args[]);

losu_extern_t losu_object_coroutine_t obj_tocoro(losu_vm_t vm,
                                                 losu_object_t obj);
#endif

#endif

/* arg api */
#if 1
losu_extern_t int32_t arg_num(losu_vm_t vm);

/* arg->get */
#if 1
losu_extern_t int32_t arg_gettype(losu_vm_t vm, int32_t idx);

losu_extern_t losu_object_t arg_get(losu_vm_t vm, int32_t idx);

losu_extern_t losu_ctype_number arg_getnum(losu_vm_t vm, int32_t idx);

losu_extern_t const char* arg_getstr(losu_vm_t vm, int32_t i);

losu_extern_t losu_vmapi_t arg_getfunc(losu_vm_t vm, int32_t i);

losu_extern_t void* arg_getptr(losu_vm_t vm, int32_t i);

#endif

/* arg->return */
#if 1
losu_extern_t void arg_return(losu_vm_t vm, losu_object obj);

losu_extern_t void arg_returnnull(losu_vm_t vm);

losu_extern_t void arg_returnnum(losu_vm_t vm, losu_ctype_number n);

losu_extern_t void arg_returnstr(losu_vm_t vm, const char* s);

losu_extern_t void arg_returnstrlen(losu_vm_t vm,
                                    const char* s,
                                    losu_ctype_size_t len);

losu_extern_t void arg_returnfunc(losu_vm_t vm, losu_vmapi_t func);

losu_extern_t void arg_returnptr(losu_vm_t vm, void* ptr);

#endif

#endif

#endif

#endif
